package com.ynet.middleground.contract.model;

import static com.ynet.middleground.contract.utils.TemplateUtil.SUFFIX;

import java.io.File;
import java.io.FileOutputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.dozer.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.util.BytePictureUtils;
import com.google.common.base.Throwables;
import com.ynet.core.common.BaseReqObj;
import com.ynet.core.common.PagerInfo;
import com.ynet.core.exception.BusinessException;
import com.ynet.core.log.IfpLogger;
import com.ynet.middleground.contract.bean.*;
import com.ynet.middleground.contract.constant.*;
import com.ynet.middleground.contract.dao.TemplateMapper;
import com.ynet.middleground.contract.dto.TemplateDTO;
import com.ynet.middleground.contract.dto.TemplateDetailDTO;
import com.ynet.middleground.contract.dto.TemplateParamDTO;
import com.ynet.middleground.contract.entity.Template;
import com.ynet.middleground.contract.entity.TemplateParam;
import com.ynet.middleground.contract.entity.TemplateTableParam;
import com.ynet.middleground.contract.policy.HackLoopTableRenderPolicy;
import com.ynet.middleground.contract.utils.BeanUtil;
import com.ynet.middleground.contract.utils.CommUtils;
import com.ynet.middleground.contract.utils.EnumUtil;
import com.ynet.middleground.contract.utils.TemplateUtil;

/**
 * 模板工具类
 *
 * @author yangbh
 */
@Component
public class TemplateModel extends ServiceImpl<TemplateMapper, Template> {

    @Autowired
    private Configure configure;

    @Autowired
    private TemplateParamModel templateParamModel;

    @Autowired
    private TemplateMapper templateMapper;

    @Autowired
    private TemplateTableParamModel templateTableParamModel;

    @Autowired
    private HackLoopTableRenderPolicy hackLoopTableRenderPolicy;

    @Autowired
    private Mapper mapper;

    @Autowired
    private TemplateOperationLogModel templateOperationLogModel;

    @Autowired
    private ContractTypeModel contractTypeModel;

    @Autowired
    private ContractManagementModel contractManagementModel;

    @Autowired
    private CommUtils commUtils;

    /**
     * 用户中心服务调用模块
     */
    @Autowired
    UserCenterModel userCenterModel;

    /**
     * 生成 word 文件 <br />
     * 非制式类可以直接使用
     *
     * @param templateUrl 模板文件完整地址
     * @param datas 模板数据 模板参数及参数值，如果需要生成二维码、合约编号，需要提前处理好放入该集合中
     * @return word 文件
     */
    public File generateWord(String templateUrl, Map<String, Object> datas) {
        File file = null;
        FileOutputStream out = null;
        XWPFTemplate template = null;
        try {
            template = XWPFTemplate.compile(TemplateUtil.getFileByUrl(templateUrl, SUFFIX), configure).render(datas);
            file = File.createTempFile("pattern", SUFFIX);
            out = new FileOutputStream(file);
            template.write(out);
            out.flush();
            out.close();
            template.close();
        } catch (Exception e) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(), "模板生成文件服务异常, case: {}",
                Throwables.getStackTraceAsString(e));
            throw new BusinessException("生成合约文件异常" + e.getCause(), "ECCT0004");
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (template != null) {
                    template.close();
                }
            } catch (Exception e) {
                IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(), "模板生成文件服务异常, case: {}",
                    Throwables.getStackTraceAsString(e));
            }
        }
        return file;
    }

    /**
     * 利用模板生成文件 <br />
     * 制式类使用
     *
     * @param templateId 模板 id
     * @param reqParams 模板参数及参数值，如果需要生成二维码、合约编号，需要提前处理好放入该集合中
     * @param tableMergeCells 表格合并单元格信息
     * @return 文件
     */
    public File generateContract(Integer templateId, Map<String, Object> reqParams,
        List<MergeCellTableInfo> tableMergeCells) {
        // 当前模板 id 对应的模板信息
        // Integer templateId = req.getTemplateId();
        // Map<String, Object> reqParams = req.getTemplateParams();
        // 当前模板 id 对应的模板信息
        Template template = this.getById(templateId);
        Optional.ofNullable(template).orElseThrow(() -> new BusinessException("合约模板不存在", "ECCT0100"));
        if (template.getIsDeleted()) {
            throw new BusinessException("无效的合约模板", "ECCT0113");
        }
        if (!TemplateStatusEnum.ACTIVATED.getCode().equals(template.getStatus())) {
            throw new BusinessException("当前合约模板未启用", "ECCT0117");
        }
        // 判断模板参数对应模板 id 对应的模板是否与最新版本冲突，防止使用旧模板
        if (TemplateStatusEnum.EXPIRE.getCode().equals(template.getStatus())) {
            throw new BusinessException("请使用当前合约模板最新版本", "ECCT0105");
        }

        LambdaQueryWrapper<TemplateParam> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TemplateParam::getTemplateId, templateId);
        queryWrapper.eq(TemplateParam::getIsDeleted, SystemConsts.NOT_DELETED);
        // 查询出该模板所需要的参数列表
        List<TemplateParam> templateParamList = templateParamModel.list(queryWrapper);
        Map<String, TemplateParam> paramsMap =
            templateParamList.stream().collect(Collectors.toMap(TemplateParam::getParamName, account -> account));
        Map<String, Object> renderParamData = renderParamData(reqParams, paramsMap, tableMergeCells);
        // 短路径转为完整路径
        String ossFullUrl = commUtils.getOssFullUrl(template.getContent());
        return generateWord(ossFullUrl, renderParamData);
    }

    /**
     * 处理渲染数据
     *
     * @param reqParams 请求参数列表
     * @param templateParams 模板对应参数列表
     * @param tableMergeCells 表格单元格合并数据信息
     * @return 处理后的渲染数据
     */
    public Map<String, Object> renderParamData(Map<String, Object> reqParams, Map<String, TemplateParam> templateParams,
        List<MergeCellTableInfo> tableMergeCells) {
        Map<String, MergeCellTableInfo> tableMergeMap = Collections.emptyMap();
        if (null != tableMergeCells && !tableMergeCells.isEmpty()) {
            tableMergeMap = tableMergeCells.stream()
                .collect(Collectors.toMap(MergeCellTableInfo::getTableName, t -> t, (v1, v2) -> v1));
        }
        Map<String, MergeCellTableInfo> finalTableMergeMap = tableMergeMap;
        reqParams.forEach((x, y) -> {
            if (templateParams.containsKey(x)) {
                TemplateParam param = templateParams.get(x);
                // 获取参数对应的类型
                TemplateParamTypeEnum paramTypeEnum = EnumUtil.getEnumObject(TemplateParamTypeEnum.class,
                    e -> e.getCode().equals(param.getType()), "模板参数类型参数值错误", "ECCT0203");
                switch (paramTypeEnum) {
                    // 表格
                    case TABLE:
                        Object renderParamData = reqParams.get(x);
                        Map<String, Object> tableData = new HashMap<String, Object>() {
                            {
                                put("renderParamData", renderParamData);
                            }
                        };
                        if (finalTableMergeMap.containsKey(x)) {
                            tableData.put("mergeCellsData", finalTableMergeMap.get(x));
                        }
                        reqParams.put(x, tableData);
                        configure.customPolicy(x, hackLoopTableRenderPolicy);
                        break;
                    // 图片
                    case PICTURE:
                        int width =
                            param.getWidth() == null ? TemplateConsts.TPL_PARAM_QR_CODE_WEIGHT : param.getWidth();
                        int height =
                            param.getHeight() == null ? TemplateConsts.TPL_PARAM_QR_CODE_HEIGHT : param.getHeight();
                        // update by liwq on 2021-10-12 start
                        // 图片类型的参数，上送什么格式，就渲染成什么格式
                        String url = String.valueOf(y);
                        String suffix = null;
                        if (StringUtils.isNotEmpty(url)) {
                            suffix = url.substring(url.lastIndexOf("."));
                        }
                        reqParams.put(x, new PictureRenderData(width, height, suffix,
                            BytePictureUtils.getUrlByteArray(String.valueOf(y))));
                        // reqParams.put(x, new PictureRenderData(width, height, ".png",
                        // BytePictureUtils.getUrlByteArray(String.valueOf(y))));
                        // update by liwq on 2021-10-12 end
                        break;
                    // 合约编号二维码
                    case CONTRACT_NO_QR_CODE:
                        // update by liwq on 2021-10-12 start
                        // 二维码宽高
                        int q_width =
                            param.getWidth() == null ? TemplateConsts.TPL_PARAM_QR_CODE_WEIGHT : param.getWidth();
                        int q_height =
                            param.getHeight() == null ? TemplateConsts.TPL_PARAM_QR_CODE_HEIGHT : param.getHeight();
                        reqParams.put(x, new PictureRenderData(q_width, q_height, ".png", (byte[])y));
                        // update by liwq on 2021-10-12 end
                        break;
                    default:
                        break;
                }
            }
        });
        return reqParams;
    }

    /**
     * 新增合约模板
     *
     * @param req 新增模板请求类
     * @return 模板 id
     */
    @Transactional(rollbackFor = Exception.class)
    public Integer addTemplate(AddTemplateReq req) {
        // 若上送业态 id，则查询业态是否有效
        if (null != req.getFormatId()) {
            userCenterModel.getFormatOrgInfo(req.getFormatId(), req);
        }

        List<TemplateParamReq> params = req.getParams();
        // 校验上送的产品类型、合约类型
        validTemplateTypeInfo(req.getSignatureNumber(), req.getType(), req.getProductType(), req.getContractTypeId(),
            req.getChannel());

        Template template = new Template();
        template.setName(req.getName());
        template.setType(req.getType());
        template.setProductType(req.getProductType());
        template.setContractTypeId(req.getContractTypeId());
        template.setDescription(req.getDescription());
        template.setContent(req.getContent());
        template.setStatus(TemplateStatusEnum.NOT_ACTIVATE.getCode());
        template.setSignatureNumber(req.getSignatureNumber());
        template.setIsDeleted(false);
        template.setChannel(req.getChannel());
        template.setGmtCreate(LocalDateTime.now());
        template.setCreateBy(req.getOperationUserId());
        template.setGmtModified(LocalDateTime.now());
        template.setModifiedBy(req.getOperationUserId());
        // 业态ID
        template.setFormatId(req.getFormatId());
        // 检查数据有效性
        checkTemplateData(template);

        // 根据模板信息查询当前最新版本，判断是否已有该模板
        LambdaQueryWrapper<Template> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Template::getIsDeleted, SystemConsts.NOT_DELETED);
        queryWrapper.ne(Template::getStatus, TemplateStatusEnum.EXPIRE.getCode());
        queryWrapper.eq(Template::getChannel, req.getChannel());
        queryWrapper.eq(Template::getName, req.getName());
        queryWrapper.eq(Template::getType, req.getType());
        if (null != req.getFormatId()) {
            queryWrapper.eq(Template::getFormatId, req.getFormatId());
        } else {
            queryWrapper.isNull(Template::getFormatId);
        }
        int count = this.count(queryWrapper);
        if (count > 0) {
            throw new BusinessException("该合约模板已存在", "ECCT0110");
        }

        boolean save = this.save(template);
        if (!save) {
            throw new BusinessException("保存模板失败", "ECCT0103");
        }
        if (null != params && !params.isEmpty()) {
            // 检查模板参数是否有重名
            TemplateParamModel.checkRepeatParams(params);
            // 校验只能存在 1 个合约编号参数
            List<String> contractNoTypeList = params.stream().map(TemplateParamReq::getType)
                .filter(type -> TemplateParamTypeEnum.CONTRACT_NO.getCode().equals(type)).collect(Collectors.toList());
            if (contractNoTypeList.size() > 1) {
                throw new BusinessException("合约编号参数已存在", "ECCT0204");
            }
            // 校验只能存在 1 个合约编号二维码参数
            List<String> contractNoQrTypeList = params.stream().map(TemplateParamReq::getType)
                .filter(type -> TemplateParamTypeEnum.CONTRACT_NO_QR_CODE.getCode().equals(type))
                .collect(Collectors.toList());
            if (contractNoQrTypeList.size() > 1) {
                throw new BusinessException("合约编号二维码参数已存在", "ECCT0205");
            }
            // 封装参数其余信息
            params.forEach(reqParam -> {
                TemplateParam param = mapper.map(reqParam, TemplateParam.class);
                param.setTemplateId(template.getId());
                param.setChannel(req.getChannel());
                param.setCreateBy(req.getOperationUserId());
                param.setGmtCreate(LocalDateTime.now());
                param.setModifiedBy(req.getOperationUserId());
                param.setGmtModified(LocalDateTime.now());
                param.setFormatId(req.getFormatId());
                // 检查参数类型值的有效性
                templateParamModel.checkTemplateParamData(param);
                templateParamModel.save(param);
                if (TemplateParamTypeEnum.TABLE.getCode().equals(param.getType())) {
                    List<TemplateTableParamReq> tableParams = reqParam.getTableParams();
                    if (tableParams != null && !tableParams.isEmpty()) {
                        List<String> nameList = templateTableParamModel.checkRepeatParams(tableParams);
                        List<TemplateTableParam> templateTableParams =
                            templateTableParamModel.convertTableParam(param.getId(), nameList, tableParams, req);
                        templateTableParamModel.saveBatch(templateTableParams);
                    }
                }
            });
        }
        // 记录模板操作日志
        templateOperationLogModel.insertOperationLog(template, OperationTypeEnum.CREATE, req);
        return template.getId();
    }

    /**
     * 保存历史模板信息
     *
     * @param templateId 模板 id
     * @param params 最新模板参数列表
     * @param baseReqObj 基础请求对象
     * @return 最新模板 id
     */
    @Transactional(rollbackFor = Exception.class)
    public Integer saveHistoryRecord(Integer templateId, List<TemplateParam> params, BaseReqObj baseReqObj,
        Integer formatId) {
        // 判断传入模板 id 对应的模板是否与最新版本冲突，防止传入的是旧模板
        Template lastestTemplate = getOnlyLastestTemplate(templateId, baseReqObj);
        if (lastestTemplate != null) {
            // 拷贝旧模板信息，并将版本号加 1，作为最新模板
            Template newLastestTemplate = mapper.map(lastestTemplate, Template.class);
            newLastestTemplate.setId(null);
            newLastestTemplate.setIsDeleted(false);
            newLastestTemplate.setPrevId(templateId);
            // 默认为未启用状态
            newLastestTemplate.setStatus(TemplateStatusEnum.NOT_ACTIVATE.getCode());
            newLastestTemplate.setGmtInactive(null);
            newLastestTemplate.setVersion(lastestTemplate.getVersion() + 1);
            newLastestTemplate.setGmtActive(null);
            newLastestTemplate.setGmtCreate(LocalDateTime.now());
            newLastestTemplate.setCreateBy(baseReqObj.getOperationUserId());
            newLastestTemplate.setModifiedBy(baseReqObj.getOperationUserId());
            newLastestTemplate.setGmtModified(LocalDateTime.now());
            newLastestTemplate.setFormatId(formatId);

            // 更新旧模板标志
            lastestTemplate.setStatus(TemplateStatusEnum.EXPIRE.getCode());
            this.updateById(lastestTemplate);
            this.save(newLastestTemplate);

            // if (null != params && !params.isEmpty()) {
            // List<TemplateParam> paramList = params.stream().map(param -> {
            // param.setId(null);
            // param.setTemplateId(newLastestTemplate.getId());
            // return param;
            // }).collect(Collectors.toList());
            // boolean result = templateParamModel.saveBatch(paramList);
            // if (!result) {
            // throw new BusinessException("新增模板参数失败", "ECCT0201");
            // }
            // }

            // 最新模板参数信息
            if (null != params && !params.isEmpty()) {
                params.forEach(param -> {
                    // 判断模板参数是否是表格类型，如果是则需要将表格参数也产生新版本
                    if (TemplateParamTypeEnum.TABLE.getCode().equals(param.getType())) {
                        // 先查询旧表格参数
                        List<TemplateTableParam> templateTableParams = templateTableParamModel
                            .listTemplateTableParamsByTableId(param.getId(), baseReqObj, formatId);
                        // 先保存新版本的表格信息
                        param.setId(null);
                        param.setTemplateId(newLastestTemplate.getId());
                        param.setFormatId(formatId);
                        templateParamModel.save(param);

                        // 处理新的表格参数信息
                        List<TemplateTableParam> newTableParams = templateTableParams.stream().peek(tableParam -> {
                            tableParam.setId(null);
                            tableParam.setTableId(param.getId());
                            tableParam.setFormatId(formatId);
                        }).collect(Collectors.toList());
                        templateTableParamModel.saveBatch(newTableParams);
                    } else {
                        param.setId(null);
                        param.setTemplateId(newLastestTemplate.getId());
                        param.setFormatId(formatId);
                        templateParamModel.save(param);
                    }
                });
            }

            return newLastestTemplate.getId();
        } else {
            throw new BusinessException("请使用当前合约模板最新版本", "ECCT0105");
        }
    }

    /**
     * 将已停用的模板重新启用
     *
     * @param baseReqObj 基础请求对象
     * @param newTemplate 最新模板对象信息
     * @param oldTemplate 旧模板信息
     * @return 最新模板 id
     */
    @Transactional(rollbackFor = Exception.class)
    public Integer saveHistoryRecordAndReEnableTemplate(EnterpriseBaseReqObj baseReqObj, Template newTemplate,
        Template oldTemplate, Integer formatId) {
        // 设置最新模板信息
        newTemplate.setId(null);
        newTemplate.setGmtInactive(null);
        newTemplate.setGmtActive(LocalDateTime.now());
        newTemplate.setCreateBy(oldTemplate.getCreateBy());
        newTemplate.setGmtCreate(oldTemplate.getGmtCreate());
        newTemplate.setModifiedBy(baseReqObj.getOperationUserId());
        newTemplate.setGmtModified(LocalDateTime.now());
        newTemplate.setIsDeleted(false);
        newTemplate.setPrevId(oldTemplate.getId());
        // 设置为已启用状态
        newTemplate.setStatus(TemplateStatusEnum.ACTIVATED.getCode());
        newTemplate.setVersion(oldTemplate.getVersion() + 1);
        newTemplate.setChannel(oldTemplate.getChannel());
        // 更新旧模板标志
        oldTemplate.setStatus(TemplateStatusEnum.EXPIRE.getCode());
        this.updateById(oldTemplate);
        this.save(newTemplate);
        LambdaQueryWrapper<TemplateParam> paramQueryWrapper = new LambdaQueryWrapper<>();
        paramQueryWrapper.eq(TemplateParam::getTemplateId, oldTemplate.getId());
        paramQueryWrapper.eq(TemplateParam::getIsDeleted, SystemConsts.NOT_DELETED);
        // 查询出当前模板参数信息
        List<TemplateParam> params = templateParamModel.list(paramQueryWrapper);
        if (null != params && !params.isEmpty()) {
            params.forEach(param -> {
                // 判断模板参数是否是表格类型，如果是则需要将表格参数也产生新版本
                if (TemplateParamTypeEnum.TABLE.getCode().equals(param.getType())) {
                    // 先查询旧表格参数
                    List<TemplateTableParam> templateTableParams =
                        templateTableParamModel.listTemplateTableParamsByTableId(param.getId(), baseReqObj, formatId);
                    // 先保存新版本的表格信息
                    param.setId(null);
                    param.setTemplateId(newTemplate.getId());
                    templateParamModel.save(param);

                    // 处理新的表格参数信息
                    List<TemplateTableParam> newTableParams = templateTableParams.stream().peek(tableParam -> {
                        tableParam.setId(null);
                        tableParam.setTableId(param.getId());
                    }).collect(Collectors.toList());
                    templateTableParamModel.saveBatch(newTableParams);
                } else {
                    param.setId(null);
                    param.setTemplateId(newTemplate.getId());
                    templateParamModel.save(param);
                }
            });
        }
        return newTemplate.getId();
    }

    /**
     * 保存历史模板信息 <br />
     * 模板信息要保存 3 部分：模板基础信息、模板参数信息、模板参数中表格的参数信息
     *
     * @param baseReqObj 基础请求对象
     * @param newTemplate 最新模板对象信息
     * @param oldTemplate 旧模板信息
     * @return 最新模板 id
     */
    @Transactional(rollbackFor = Exception.class)
    public Integer saveHistoryRecord(EnterpriseBaseReqObj baseReqObj, Template newTemplate, Template oldTemplate,
        Integer formatId) {
        // 设置最新模板信息
        newTemplate.setId(null);
        newTemplate.setGmtInactive(null);
        newTemplate.setGmtActive(null);
        newTemplate.setCreateBy(oldTemplate.getCreateBy());
        newTemplate.setGmtCreate(oldTemplate.getGmtCreate());
        newTemplate.setModifiedBy(baseReqObj.getOperationUserId());
        newTemplate.setGmtModified(LocalDateTime.now());
        newTemplate.setIsDeleted(false);
        newTemplate.setPrevId(oldTemplate.getId());
        // 默认为未启用状态
        newTemplate.setStatus(TemplateStatusEnum.NOT_ACTIVATE.getCode());
        newTemplate.setVersion(oldTemplate.getVersion() + 1);
        newTemplate.setChannel(oldTemplate.getChannel());
        // 更新旧模板标志
        oldTemplate.setStatus(TemplateStatusEnum.EXPIRE.getCode());
        this.updateById(oldTemplate);
        this.save(newTemplate);
        LambdaQueryWrapper<TemplateParam> paramQueryWrapper = new LambdaQueryWrapper<>();
        paramQueryWrapper.eq(TemplateParam::getTemplateId, oldTemplate.getId());
        paramQueryWrapper.eq(TemplateParam::getIsDeleted, SystemConsts.NOT_DELETED);
        // 查询出当前模板参数信息
        List<TemplateParam> params = templateParamModel.list(paramQueryWrapper);
        if (null != params && !params.isEmpty()) {
            params.forEach(param -> {
                // 判断模板参数是否是表格类型，如果是则需要将表格参数也产生新版本
                if (TemplateParamTypeEnum.TABLE.getCode().equals(param.getType())) {
                    // 先查询旧表格参数
                    List<TemplateTableParam> templateTableParams =
                        templateTableParamModel.listTemplateTableParamsByTableId(param.getId(), baseReqObj, formatId);
                    // 先保存新版本的表格信息
                    param.setId(null);
                    param.setTemplateId(newTemplate.getId());
                    templateParamModel.save(param);

                    // 处理新的表格参数信息
                    List<TemplateTableParam> newTableParams = templateTableParams.stream().peek(tableParam -> {
                        tableParam.setId(null);
                        tableParam.setTableId(param.getId());
                    }).collect(Collectors.toList());
                    templateTableParamModel.saveBatch(newTableParams);
                } else {
                    param.setId(null);
                    param.setTemplateId(newTemplate.getId());
                    templateParamModel.save(param);
                }
            });
            /*boolean result = templateParamModel.saveBatch(paramList);
            if (!result) {
                throw new BusinessException("新增模板参数失败", "ECCT0201");
            }*/
        }
        return newTemplate.getId();
    }

    /**
     * 只获取指定模板 id 的最新版本
     *
     * @param templateId 当前模板 id
     * @param baseReqObj 请求基础参数
     * @return 返回当前最新模板的信息，为空则不是当前最新版
     */
    public Template getOnlyLastestTemplate(Integer templateId, BaseReqObj baseReqObj) {
        // 当前模板 id 对应的模板信息
        Template template = this.getById(templateId);
        Optional.ofNullable(template).orElseThrow(() -> new BusinessException("合约模板不存在", "ECCT0100"));
        if (template.getIsDeleted()) {
            throw new BusinessException("无效的合约模板", "ECCT0113");
        }
        // 跨渠道访问交由内管控制 不由渠道端自行控制 by yangbh in 20200708 start
        /*if (!SystemConsts.CHANNEL_INTERNAL_MANAGEMENT.equals(baseReqObj.getChannel())) {
            if (!template.getChannel().equals(baseReqObj.getChannel())) {
                throw new BusinessException("当前操作渠道与数据添加渠道不一致", "ECCT0003");
            }
        }*/
        // 跨渠道访问交由内管控制 不由渠道端自行控制 by yangbh in 20200708 end
        /*// 根据模板信息查询当前最新版本
        Template lastestTemplate = templateMapper.getLastest(template);
        // 判断传入模板 id 对应的模板是否与最新版本冲突
        if (lastestTemplate.getId().equals(template.getId()) || template.getVersion() >= lastestTemplate.getVersion()) {
            return lastestTemplate;
        }*/
        LambdaQueryWrapper<Template> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Template::getIsDeleted, SystemConsts.NOT_DELETED);
        queryWrapper.ne(Template::getStatus, TemplateStatusEnum.EXPIRE.getCode());
        queryWrapper.eq(Template::getChannel, baseReqObj.getChannel());
        queryWrapper.eq(Template::getName, template.getName());
        queryWrapper.eq(Template::getType, template.getType());
        if (null != template.getFormatId()) {
            queryWrapper.eq(Template::getFormatId, template.getFormatId());
        } else {
            queryWrapper.isNull(Template::getFormatId);
        }
        Template lastestTemplate = this.getOne(queryWrapper);
        if (TemplateStatusEnum.EXPIRE.getCode().equals(lastestTemplate.getStatus())) {
            return null;
        }
        return lastestTemplate;
    }

    /**
     * 检查模板数据参数值的有效性，有误则抛出异常
     *
     * @param template 模板对象
     */
    public void checkTemplateData(Template template) {
        // 模板类型 0：合同模板、1：平台协议模板、2：档案模板
        String type = template.getType();
        if (StringUtils.isNotBlank(type) && !TemplateConsts.TPL_TYPE_CONTRACT.equals(type)
            && !TemplateConsts.TPL_TYPE_AGREEMENT.equals(type) && !TemplateConsts.TPL_TYPE_ARCHIVE.equals(type)) {
            throw new BusinessException("模板类型参数值错误", "ECCT0106");
        }
        // 模板种类 0：标准合同（制式合同）、1：非标准合同（非制式合同）
        String kind = template.getKind();
        if (StringUtils.isNotBlank(kind) && !TemplateConsts.TPL_KIND_STANDARD.equals(kind)
            && !TemplateConsts.TPL_KIND_NON_STANDARD.equals(kind)) {
            throw new BusinessException("模板种类参数值错误", "ECCT0107");
        }
        // 模板状态值有效性检查 0：未启用，1：启用中，2：已启用，3：已停用，4：已过期
        String status = template.getStatus();
        if (StringUtils.isNotBlank(status)) {
            Optional<TemplateStatusEnum> paramTypeEnum =
                EnumUtil.getEnumObject(TemplateStatusEnum.class, e -> e.getCode().equals(status));
            paramTypeEnum.orElseThrow(() -> new BusinessException("模板状态参数值错误", "ECCT0108"));
        }
    }

    /**
     * 根据查询条件分页查询合约模板列表
     *
     * @param reqPager 分页查询模板信息请求信息
     * @return 合约模板列表
     */
    public List<TemplateDTO> listTemplates(QueryTemplateReqPager reqPager) {
        // // 若上送业态 id，则查询业态是否有效
        // if (null != reqPager.getFormatId()) {
        // userCenterModel.getFormatOrgInfo(reqPager.getFormatId(), reqPager);
        // }
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        // 创建时间
        if (StringUtils.isNotBlank(reqPager.getCreateTimeStart())) {
            try {
                dateFormat.parse(reqPager.getCreateTimeStart());
                reqPager.setCreateTimeStart(reqPager.getCreateTimeStart() + " 00:00:00");
            } catch (ParseException e) {
                throw new BusinessException("创建时间起格式不正确", "ECCT0109");
            }
        }
        if (StringUtils.isNotBlank(reqPager.getCreateTimeEnd())) {
            try {
                dateFormat.parse(reqPager.getCreateTimeEnd());
                reqPager.setCreateTimeEnd(reqPager.getCreateTimeEnd() + " 23:59:59");
            } catch (ParseException e) {
                throw new BusinessException("创建时间止格式不正确", "ECCT0109");
            }
        }
        // 启用时间
        if (StringUtils.isNotBlank(reqPager.getActiveTimeStart())) {
            try {
                dateFormat.parse(reqPager.getActiveTimeStart());
                reqPager.setActiveTimeStart(reqPager.getActiveTimeStart() + " 00:00:00");
            } catch (ParseException e) {
                throw new BusinessException("启用时间起格式不正确", "ECCT0109");
            }
        }
        if (StringUtils.isNotBlank(reqPager.getActiveTimeEnd())) {
            try {
                dateFormat.parse(reqPager.getActiveTimeEnd());
                reqPager.setActiveTimeEnd(reqPager.getActiveTimeEnd() + " 23:59:59");
            } catch (ParseException e) {
                throw new BusinessException("启用时间止格式不正确", "ECCT0109");
            }
        }

        // 跨渠道访问交由内管控制 不由渠道端自行控制 by yangbh in 20200715 start
        // 如果渠道是中台内管，则不添加渠道限制条件
        // if (SystemConsts.CHANNEL_INTERNAL_MANAGEMENT.equals(reqPager.getChannel())) {
        // reqPager.setChannel(null);
        // }
        // 跨渠道访问交由内管控制 不由渠道端自行控制 by yangbh in 20200715 end

        // 检查模板数据参数值的有效性
        Template template = new Template();
        template.setType(reqPager.getType());
        template.setStatus(reqPager.getStatus());
        checkTemplateData(template);

        long count = templateMapper.countTemplates(reqPager);
        PagerInfo pagerInfo = reqPager.getPagerInfo();
        pagerInfo.setTotal(count);
        reqPager.setPagerInfo(pagerInfo);

        List<Template> templates = templateMapper.listTemplates(reqPager);
        return BeanUtil.mapList(mapper, templates, TemplateDTO.class);
    }

    /**
     * 根据查询条件分页查询合约模板列表
     *
     * <pre>
     *     该方法后期调整时使用，使用 MyBatis 自带 API 操作
     * </pre>
     *
     * @param reqPager 分页查询模板信息请求信息
     * @return 合约模板列表
     */
    public List<TemplateDTO> listTemplatesPage(QueryTemplateReqPager reqPager) {
        LambdaQueryWrapper<Template> queryWrapper = new LambdaQueryWrapper<>();
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        // 创建时间
        if (StringUtils.isNotBlank(reqPager.getCreateTimeStart())) {
            try {
                dateFormat.parse(reqPager.getCreateTimeStart());
                queryWrapper.ge(Template::getGmtCreate, reqPager.getCreateTimeStart() + " 00:00:00");
            } catch (ParseException e) {
                throw new BusinessException("创建时间起格式不正确", "ECCT0109");
            }
        }
        if (StringUtils.isNotBlank(reqPager.getCreateTimeEnd())) {
            try {
                dateFormat.parse(reqPager.getCreateTimeEnd());
                queryWrapper.le(Template::getGmtCreate, reqPager.getCreateTimeEnd() + " 23:59:59");
            } catch (ParseException e) {
                throw new BusinessException("创建时间止格式不正确", "ECCT0109");
            }
        }
        // 启用时间
        if (StringUtils.isNotBlank(reqPager.getActiveTimeStart())) {
            try {
                dateFormat.parse(reqPager.getActiveTimeStart());
                queryWrapper.ge(Template::getGmtActive, reqPager.getActiveTimeStart() + " 00:00:00");
            } catch (ParseException e) {
                throw new BusinessException("启用时间起格式不正确", "ECCT0109");
            }
        }
        if (StringUtils.isNotBlank(reqPager.getActiveTimeEnd())) {
            try {
                dateFormat.parse(reqPager.getActiveTimeEnd());
                queryWrapper.ge(Template::getGmtActive, reqPager.getActiveTimeEnd() + " 23:59:59");
            } catch (ParseException e) {
                throw new BusinessException("启用时间止格式不正确", "ECCT0109");
            }
        }

        // 这里不能写上该条件，否则根据跨渠道功能会失效
        // queryWrapper.eq(Template::getChannel, reqPager.getChannel());

        // 检查模板数据参数值的有效性
        Template template = new Template();
        template.setType(reqPager.getType());
        template.setStatus(reqPager.getStatus());
        checkTemplateData(template);
        Page<Template> iPage = new Page<>(reqPager.getPagerInfo().getPageNum(), reqPager.getPagerInfo().getPageSize());
        IPage<Template> templateIPage = this.page(iPage, queryWrapper);
        List<Template> templates = templateIPage.getRecords();
        PagerInfo pagerInfo = reqPager.getPagerInfo();
        pagerInfo.setTotal(templateIPage.getTotal());
        reqPager.setPagerInfo(pagerInfo);
        return BeanUtil.mapList(mapper, templates, TemplateDTO.class);
    }

    /**
     * 根据模板 id 查询模板详情信息
     *
     * @param req 查询指定模板详情信息请求对象
     * @return 模板详情信息
     */
    public TemplateDetailDTO getTemplateById(QueryTemplateReq req) {
        // 当前模板 id 对应的模板信息
        Template template = this.getById(req.getTemplateId());
        Optional.ofNullable(template).orElseThrow(() -> new BusinessException("合约模板不存在", "ECCT0100"));
        if (template.getIsDeleted()) {
            throw new BusinessException("无效的合约模板", "ECCT0113");
        }

        // // 若上送业态 id，则查询业态是否有效
        // if (null != req.getFormatId()) {
        // userCenterModel.getFormatOrgInfo(req.getFormatId(), req);
        // }

        // 跨渠道访问交由内管控制 不由渠道端自行控制 by yangbh in 20200708 start
        // 如果渠道是中台内管，则不添加渠道限制条件
        /*if (!SystemConsts.CHANNEL_INTERNAL_MANAGEMENT.equals(req.getChannel())) {
            if (!template.getChannel().equals(req.getChannel())) {
                throw new BusinessException("当前操作渠道与数据添加渠道不一致", "ECCT0003");
            }
        }*/
        // 跨渠道访问交由内管控制 不由渠道端自行控制 by yangbh in 20200708 end

        // 查询模板对应参数列表
        QueryTemplateParamsReq queryTemplateParamsReq = new QueryTemplateParamsReq();
        queryTemplateParamsReq.setTemplateId(req.getTemplateId());
        queryTemplateParamsReq.setChannel(req.getChannel());
        queryTemplateParamsReq.setOperationUserId(req.getOperationUserId());
        queryTemplateParamsReq.setRequestTime(req.getRequestTime());
        queryTemplateParamsReq.setTraceId(req.getTraceId());
        List<TemplateParamDTO> paramsList = templateParamModel.queryTemplateParamsByTemplateId(queryTemplateParamsReq);
        // 封装模板详情信息对象
        TemplateDetailDTO templateDetailDTO = new TemplateDetailDTO();
        TemplateDTO templateDTO = mapper.map(template, TemplateDTO.class);
        templateDetailDTO.setTemplate(templateDTO);
        templateDetailDTO.setParamsList(paramsList);
        return templateDetailDTO;
    }

    /**
     * 根据模板 id 删除指定模板
     *
     * @param req 删除模板请求类
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteTemplateById(DeleteTemplateReq req) {
        List<Integer> templateIds = req.getTemplateIds();
        if (templateIds != null && !templateIds.isEmpty()) {
            templateIds.forEach(tid -> {
                Template template = this.getById(tid);
                Optional.ofNullable(template).orElseThrow(() -> new BusinessException("合约模板不存在", "ECCT0100"));
                if (template.getIsDeleted()) {
                    throw new BusinessException("无效的合约模板", "ECCT0113");
                }

                // add by liwq on 20220921 start
                // 模板已关联业态，修改时却未上送业态，则不允许修改
                if (template.getFormatId() != null && req.getFormatId() == null) {
                    throw new BusinessException("该模板已关联业态信息，请上送所属业态id！", "ECCT0009");
                }
                // 修改时上送的业态与模板关联业态的不一致，则不允许修改
                if (null != req.getFormatId() && !req.getFormatId().equals(template.getFormatId())) {
                    throw new BusinessException("该模板关联业态信息与上送业态id不一致，请检查！", "ECCT0008");
                }
                // add by liwq on 20220921 end

                // 跨渠道访问交由内管控制 不由渠道端自行控制 by yangbh in 20200708 start
                /*if (!template.getChannel().equals(req.getChannel())) {
                    throw new BusinessException("当前操作渠道与数据添加渠道不一致", "ECCT0003");
                }*/
                // 跨渠道访问交由内管控制 不由渠道端自行控制 by yangbh in 20200708 end
                if (!TemplateStatusEnum.NOT_ACTIVATE.getCode().equals(template.getStatus())) {
                    throw new BusinessException("只有未启用的模板才可删除", "ECCT0111");
                }
                template.setGmtModified(LocalDateTime.now());
                template.setModifiedBy(req.getOperationUserId());
                template.setIsDeleted(true);
                this.updateById(template);
                // 一并删除模板参数
                templateParamModel.deleteTeplateParamsByTemplateId(tid, req);
                // 记录模板操作日志
                templateOperationLogModel.insertOperationLog(template, OperationTypeEnum.DELETE, req);
            });
        }
    }

    /**
     * 更新模板状态
     *
     * @param req
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateTemplateStatusById(UpdateTemplateStatusForChannelReq req) {
        Integer templateId = req.getTemplateId();
        String statusCode = req.getStatusCode();

        // 当前模板 id 对应的模板信息
        Template template = this.getById(templateId);
        // LambdaQueryWrapper<Template> queryWrapper = new LambdaQueryWrapper<>();
        // queryWrapper.eq(Template::getId, templateId);
        // queryWrapper.eq(Template::getIsDeleted, SystemConsts.NOT_DELETED);

        Optional.ofNullable(template)
            .orElseThrow(() -> new BusinessException("[" + templateId + "]合约模板不存在", "ECCT0100"));

        // 判断模板参数对应模板 id 对应的模板是否与最新版本冲突，防止修改旧模板的参数
        if (TemplateStatusEnum.EXPIRE.getCode().equals(template.getStatus())) {
            throw new BusinessException("[" + templateId + "]请使用当前合约模板最新版本", "ECCT0105");
        }

        // add by liwq on 20220921 start
        // 档案已关联业态，修改时却未上送业态，则不允许修改
        if (template.getFormatId() != null && req.getFormatId() == null) {
            throw new BusinessException("该模板已关联业态信息，请上送所属业态id！", "ECCT0009");
        }
        // 修改时上送的业态与档案关联业态的不一致，则不允许修改
        if (null != req.getFormatId() && !req.getFormatId().equals(template.getFormatId())) {
            throw new BusinessException("该模板关联业态信息与上送业态id不一致，请检查！", "ECCT0008");
        }
        // add by liwq on 20220921 end

        switch (statusCode) {
            case "0":
                // 将模板更改为: 未启用
                // 只有启用中1，才能修改至未启用0;(注意：模板新建完之后默认就是0，也允许从启用中1，改为未启用0)
                if (!TemplateStatusEnum.ACTIVATING.getCode().equals(template.getStatus())) {
                    throw new BusinessException("[" + templateId + "]模板状态为“1启用中”才可修改状态至“0未启用”", "ECCT0111");
                } else {
                    template.setStatus(TemplateStatusEnum.NOT_ACTIVATE.getCode());
                    template.setGmtModified(LocalDateTime.now());
                }
                break;
            case "1":
                // 将模板更改为: 启用中
                // 只有未启用0||已停用3，才能修改至启用中1
                if (!TemplateStatusEnum.NOT_ACTIVATE.getCode().equals(template.getStatus())
                    && !TemplateStatusEnum.DISABLED.getCode().equals(template.getStatus())) {
                    throw new BusinessException("[" + templateId + "]模板状态为“0未启用或者3已停用”才可修改状态至“1启用中”", "ECCT0112");
                } else {
                    template.setStatus(TemplateStatusEnum.ACTIVATING.getCode());
                    template.setGmtModified(LocalDateTime.now());
                }
                break;
            case "2":
                // 将模板更改为: 已启用
                // 只有启用中1，才能修改至已启用2
                if (!TemplateStatusEnum.ACTIVATING.getCode().equals(template.getStatus())) {
                    throw new BusinessException("[" + templateId + "]模板状态为“1启用中”才可修改状态至“2已启用”", "ECCT0114");
                }
                // 判断这次正式启用，是从新建——>启用，还是停用——>重新启用过来的
                // 若停用时间字段有值，则代表本模板已经停用，要重新启用。这种情况，按照需求，要重新生成一条模板数据。
                if (template.getGmtInactive() != null) {
                    Template updateTemplate = mapper.map(template, Template.class);
                    saveHistoryRecordAndReEnableTemplate(req, updateTemplate, template, req.getFormatId());
                    return;
                }
                // 若停用时间字段为空，则代表本模板是新建，然后启用
                if (template.getGmtInactive() == null) {
                    template.setStatus(TemplateStatusEnum.ACTIVATED.getCode());
                    template.setGmtActive(LocalDateTime.now());
                }
                // 记录模板操作日志
                templateOperationLogModel.insertOperationLog(template, OperationTypeEnum.ACTIVE, req);
                break;
            case "3":
                // 将模板更改为: 已停用
                // 只有已启用2，才能修改至已停用3
                if (!TemplateStatusEnum.ACTIVATED.getCode().equals(template.getStatus())) {
                    throw new BusinessException("[" + templateId + "]模板状态为“2已启用”才可修改状态至“3已停用”", "ECCT0115");
                }
                // 判断是否有在途合约
                int countProcessingContract =
                    contractManagementModel.countProcessingContractByTemplateId(templateId, req.getChannel());
                boolean isApproving = countProcessingContract > 0;
                if (isApproving) {
                    throw new BusinessException("[" + templateId + "]该模板有在途合约，请结束后再操作", "ECCT0116");
                }
                template.setStatus(TemplateStatusEnum.DISABLED.getCode());
                template.setGmtInactive(LocalDateTime.now());
                // 记录模板操作日志
                templateOperationLogModel.insertOperationLog(template, OperationTypeEnum.NOT_ACTIVATE, req);
                break;
            default:
                throw new BusinessException("模板状态参数值错误", "ECCT0108");
        }
        this.updateById(template);
    }

    /**
     * 修改模板信息
     *
     * @param req 修改模板基础信息请求类
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateTemplateById(UpdateTemplateReq req) {
        // 校验上送的产品类型、合约类型
        validTemplateTypeInfo(req.getSignatureNumber(), req.getType(), req.getProductType(), req.getContractTypeId(),
            req.getChannel());
        // 当前模板 id 对应的模板信息
        Template template = this.getById(req.getId());
        Optional.ofNullable(template).orElseThrow(() -> new BusinessException("合约模板不存在", "ECCT0100"));

        // add by liwq on 20220921 start
        // 档案已关联业态，修改时却未上送业态，则不允许修改
        if (template.getFormatId() != null && req.getFormatId() == null) {
            throw new BusinessException("该模板已关联业态信息，请上送所属业态id！", "ECCT0009");
        }
        // 修改时上送的业态与档案关联业态的不一致，则不允许修改
        if (null != req.getFormatId() && !req.getFormatId().equals(template.getFormatId())) {
            throw new BusinessException("该模板关联业态信息与上送业态id不一致，请检查！", "ECCT0008");
        }
        // add by liwq on 20220921 end

        // // 跨渠道访问交由内管控制 不由渠道端自行控制 by yangbh in 20200708 start
        /*if (!template.getChannel().equals(req.getChannel())) {
            throw new BusinessException("当前操作渠道与数据添加渠道不一致", "ECCT0003");
        }*/
        // 跨渠道访问交由内管控制 不由渠道端自行控制 by yangbh in 20200708 end
        if (template.getIsDeleted()) {
            throw new BusinessException("无效的合约模板", "ECCT0113");
        }
        // 判断模板参数对应模板 id 对应的模板是否与最新版本冲突，防止修改旧模板的参数
        if (TemplateStatusEnum.EXPIRE.getCode().equals(template.getStatus())) {
            throw new BusinessException("请使用当前合约模板最新版本", "ECCT0105");
        }
        if (!TemplateStatusEnum.NOT_ACTIVATE.getCode().equals(template.getStatus())
            && !TemplateStatusEnum.DISABLED.getCode().equals(template.getStatus())) {
            throw new BusinessException("模板状态为“未启用”或“已停用”才可修改模板信息", "ECCT0112");
        }
        LambdaQueryWrapper<Template> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Template::getIsDeleted, SystemConsts.NOT_DELETED);
        queryWrapper.ne(Template::getStatus, TemplateStatusEnum.EXPIRE.getCode());
        queryWrapper.eq(Template::getChannel, req.getChannel());
        queryWrapper.eq(Template::getName, req.getName());
        queryWrapper.eq(Template::getType, req.getType());
        if (null != req.getFormatId()) {
            queryWrapper.eq(Template::getFormatId, req.getFormatId());
        } else {
            queryWrapper.isNull(Template::getFormatId);
        }
        Template lastestTemplate = this.getOne(queryWrapper);
        if (null != lastestTemplate && !lastestTemplate.getId().equals(req.getId())) {
            throw new BusinessException("该合约模板已存在", "ECCT0110");
        }
        // 如果是已停用状态模板，需要保存历史记录
        if (TemplateStatusEnum.DISABLED.getCode().equals(template.getStatus())) {
            Template updateTemplate = mapper.map(req, Template.class);
            saveHistoryRecord(req, updateTemplate, template, req.getId());
            return;
        }
        String sourceChannel = template.getChannel();
        mapper.map(req, template);
        template.setChannel(sourceChannel);
        checkTemplateData(template);
        this.updateById(template);
        // 记录模板操作日志
        templateOperationLogModel.insertOperationLog(template, OperationTypeEnum.UPDATE, req);
    }

    /**
     * 校验模板中产品类型、合约类型是否必填
     *
     * @param signatureNumber 签章数量(合约类型不能为空)
     * @param type 模板类型 {@link com.ynet.middleground.contract.constant.TemplateConsts}
     * @param productType 产品类型
     * @param contractTypeId 合约类型 id
     * @param channel 渠道标志
     */
    private void validTemplateTypeInfo(Integer signatureNumber, String type, String productType, Integer contractTypeId,
        String channel) {
        // 合约模板必须上送产品类型、合约类型
        if (TemplateConsts.TPL_TYPE_CONTRACT.equals(type)) {
            if (StringUtils.isBlank(productType)) {
                throw new BusinessException("产品类型不能为空", "ECCT0001");
            }
            if (signatureNumber == null) {
                throw new BusinessException("签章数量不能为空", "ECCT0001");
            }
            if (contractTypeId == null) {
                throw new BusinessException("合约类型不能为空", "ECCT0001");
            } else {
                // 合约类型有效性校验
                contractTypeModel.validContractType(contractTypeId, channel);
            }
            // 档案模板必须上送产品类型
        } else if (TemplateConsts.TPL_TYPE_ARCHIVE.equals(type)) {
            if (StringUtils.isBlank(productType)) {
                throw new BusinessException("产品类型不能为空", "ECCT0001");
            }
        }
    }

    /**
     * 更新模板状态，用于激活模板、停用模板 <note>监听器修改方式废弃，该方法暂时保留，如后续使用，需要调整优化，具体参考
     * {@link TemplateModel#updateTemplateStatusById(UpdateTemplateStatusForChannelReq)}</note>
     * 
     * @param req 更新模板请求对象
     * @param statusCode 合约模板状态码 {@link TemplateStatusEnum } 1: 启用中, 2: 激活模板, 3: 停用模板
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateTemplateStatus(Integer templateId, EnterpriseBaseReqObj req, String statusCode,
        Integer formatId) {
        // 当前模板 id 对应的模板信息
        Template template = this.getById(templateId);
        Optional.ofNullable(template).orElseThrow(() -> new BusinessException("合约模板不存在", "ECCT0100"));
        // // 跨渠道访问交由内管控制 不由渠道端自行控制 by yangbh in 20200708 start
        /*if (!template.getChannel().equals(req.getChannel())) {
        Optional.ofNullable(template)
            .orElseThrow(() -> new BusinessException("[" + templateId + "]合约模板不存在", "ECCT0100"));
        if (!template.getChannel().equals(req.getChannel())) {
            throw new BusinessException("当前操作渠道与数据添加渠道不一致", "ECCT0003");
        }*/
        // // 跨渠道访问交由内管控制 不由渠道端自行控制 by yangbh in 20200708 end
        // 判断模板参数对应模板 id 对应的模板是否与最新版本冲突，防止修改旧模板的参数
        if (TemplateStatusEnum.EXPIRE.getCode().equals(template.getStatus())) {
            throw new BusinessException("[" + templateId + "]请使用当前合约模板最新版本", "ECCT0105");
        }
        if (template.getIsDeleted()) {
            throw new BusinessException("[" + templateId + "]无效的合约模板", "ECCT0113");
        }

        switch (statusCode) {
            // 将模板更改为: 启用中
            case "1":
                // 未启用状态
                if (TemplateStatusEnum.NOT_ACTIVATE.getCode().equals(template.getStatus())
                    || TemplateStatusEnum.ACTIVATING.getCode().equals(template.getStatus())
                    || TemplateStatusEnum.DISABLED.getCode().equals(template.getStatus())) {
                    template.setStatus(TemplateStatusEnum.ACTIVATING.getCode());
                    template.setGmtModified(LocalDateTime.now());
                } else {
                    throw new BusinessException("[" + templateId + "]模板状态为“未启用”或“已停用”才可修改模板为启用中", "ECCT0112");
                }
                break;
            // 将模板更改为: 已启用
            case "2":
                // 未启用状态或已停用状态
                if (TemplateStatusEnum.NOT_ACTIVATE.getCode().equals(template.getStatus())
                    || TemplateStatusEnum.ACTIVATING.getCode().equals(template.getStatus())) {
                    template.setStatus(TemplateStatusEnum.ACTIVATED.getCode());
                    template.setGmtActive(LocalDateTime.now());
                } else if (TemplateStatusEnum.DISABLED.getCode().equals(template.getStatus())) {
                    Template updateTemplate = mapper.map(template, Template.class);
                    saveHistoryRecord(req, updateTemplate, template, formatId);
                    return;
                } else {
                    throw new BusinessException("[" + templateId + "]模板状态为“未启用”或“已停用”才可启用模板", "ECCT0114");
                }
                // 记录模板操作日志
                templateOperationLogModel.insertOperationLog(template, OperationTypeEnum.ACTIVE, req);
                break;
            // 将模板更改为: 已停用
            case "3":
                if (!TemplateStatusEnum.ACTIVATED.getCode().equals(template.getStatus())) {
                    throw new BusinessException("[" + templateId + "]模板状态为“已启用”才可停用模板", "ECCT0115");
                }
                // 判断是否有在途合约
                int countProcessingContract =
                    contractManagementModel.countProcessingContractByTemplateId(templateId, req.getChannel());
                boolean isApproving = countProcessingContract > 0;
                if (isApproving) {
                    throw new BusinessException("[" + templateId + "]该模板有在途合约，请结束后再操作", "ECCT0116");
                }
                template.setStatus(TemplateStatusEnum.DISABLED.getCode());
                template.setGmtInactive(LocalDateTime.now());
                // 记录模板操作日志
                templateOperationLogModel.insertOperationLog(template, OperationTypeEnum.NOT_ACTIVATE, req);
                break;
            default:
                throw new BusinessException("模板状态参数值错误", "ECCT0108");
        }

        this.updateById(template);
    }

    /**
     * 更新模板为启用中
     *
     * @param req 更新模板状态请求类
     */
    @Transactional(rollbackFor = Exception.class)
    public void activingTemplate(UpdateTemplateStatusReq req) {
        List<Integer> templateIds = req.getTemplateIds();
        if (templateIds != null && !templateIds.isEmpty()) {
            templateIds.forEach(tid -> updateTemplateStatus(tid, req, "1", req.getFormatId()));
        }

    }

    /**
     * 启用模板
     *
     * @param req 更新模板状态请求类
     */
    @Transactional(rollbackFor = Exception.class)
    public void activeTemplate(UpdateTemplateStatusReq req) {
        List<Integer> templateIds = req.getTemplateIds();
        if (templateIds != null && !templateIds.isEmpty()) {
            templateIds.forEach(tid -> updateTemplateStatus(tid, req, "2", req.getFormatId()));
        }

    }

    /**
     * 停用模板
     *
     * @param req 更新模板状态请求类
     */
    @Transactional(rollbackFor = Exception.class)
    public void disableTemplate(UpdateTemplateStatusReq req) {
        List<Integer> templateIds = req.getTemplateIds();
        if (templateIds != null && !templateIds.isEmpty()) {
            templateIds.forEach(tid -> updateTemplateStatus(tid, req, "3", req.getFormatId()));
        }

    }

    /**
     * 判断模板是否可以变更为其他状态
     *
     * @param req 查询模板是否符合激活或停用的条件
     */
    public void checkTemplateCondition(QueryTemplatesConditionReq req) {
        List<Integer> templateIds = req.getTemplateIds();
        if (templateIds != null && !templateIds.isEmpty()) {
            templateIds.forEach(tid -> checkTemplateCondition(tid, req.getStatusCode(), req));
        }
    }

    /**
     * 判断模板是否可以变更为其他状态
     *
     * @param templateId 模板 id
     * @param statusCode 模板目标状态
     * @param req 基础请求参数
     */
    public void checkTemplateCondition(Integer templateId, String statusCode, BaseReqObj req) {
        // 当前模板 id 对应的模板信息
        Template template = this.getById(templateId);

        Optional.ofNullable(template)
            .orElseThrow(() -> new BusinessException("[" + templateId + "]合约模板不存在", "ECCT0100"));

        if (template.getIsDeleted()) {
            throw new BusinessException("[" + templateId + "]无效的合约模板", "ECCT0113");
        }

        // 判断模板参数对应模板 id 对应的模板是否与最新版本冲突，防止修改旧模板的参数
        if (TemplateStatusEnum.EXPIRE.getCode().equals(template.getStatus())) {
            throw new BusinessException("[" + templateId + "]请使用当前合约模板最新版本", "ECCT0105");
        }

        switch (statusCode) {
            // 将模板更改为: 启用中
            case "1":
                // 未启用状态
                if (!TemplateStatusEnum.NOT_ACTIVATE.getCode().equals(template.getStatus())
                    && !TemplateStatusEnum.ACTIVATING.getCode().equals(template.getStatus())
                    && !TemplateStatusEnum.DISABLED.getCode().equals(template.getStatus())) {
                    throw new BusinessException("[" + templateId + "]模板状态为“未启用”或“已停用”才可修改模板为启用中", "ECCT0112");
                }
                break;
            // 将模板更改为: 已启用
            case "2":
                // 未启用状态或已停用状态
                if (TemplateStatusEnum.NOT_ACTIVATE.getCode().equals(template.getStatus())
                    || TemplateStatusEnum.ACTIVATING.getCode().equals(template.getStatus())) {

                } else if (TemplateStatusEnum.DISABLED.getCode().equals(template.getStatus())) {
                    return;
                } else {
                    throw new BusinessException("[" + templateId + "]模板状态为“未启用”或“已停用”才可启用模板", "ECCT0114");
                }

                break;
            // 将模板更改为: 已停用
            case "3":
                if (!TemplateStatusEnum.ACTIVATED.getCode().equals(template.getStatus())) {
                    throw new BusinessException("[" + templateId + "]模板状态为“已启用”才可停用模板", "ECCT0115");
                }
                // 判断是否有在途合约
                int countProcessingContract =
                    contractManagementModel.countProcessingContractByTemplateId(templateId, req.getChannel());
                boolean isApproving = countProcessingContract > 0;
                if (isApproving) {
                    throw new BusinessException("[" + templateId + "]该模板有在途合约，请结束后再操作", "ECCT0116");
                }
                break;
            default:
                throw new BusinessException("模板状态参数值错误", "ECCT0108");
        }
    }

    /**
     * 保存历史模板信息
     *
     * <pre>
     * <h3>该方法针对已停用的模板新增表格参数处理流程</h3>
     * 接收：<b>当前最新模板对象</b>、<b>新增的表格参数列表</b>、<b>新增表格参数列表对应的表格 id</b>
     *
     * 当前最新模板对象(Template) -----复制-----> 新版本，版本号 + 1(newTemplate)
     *      ↓
     * 查询当前版本对应的模板参数列表(TemplateParams)
     *      ↓
     * 循环判断模板参数是否表格类型 -----否-----> 模板参数对应模板 id 设置为新版本模板 id，并保存
     *      ↓ 是
     * 判断是否是当前要增加表格参数的表格 -----是-----> 保存新的表格信息，将新增的表格参数列表对应的表格 id 都替换为新的表格 id，再保存新的表格参数
     *      ↓ 否
     * 先查询表格当前所有参数列表，保存新的表格信息，将表格的参数对应的表格 id 都替换为新的表格 id，再保存新的表格参数
     * </pre>
     *
     * @param latestTemplate tableId 对应的模板最新版本
     * @param tableId tableParams 对应的表格 id
     * @param tableParams 处理好的表格参数
     * @param baseReqObj 基础请求对象
     * @return 最新模板 id
     */
    @Transactional(rollbackFor = Exception.class)
    public Integer saveHistoryRecord(Template latestTemplate, Integer tableId, List<TemplateTableParam> tableParams,
        BaseReqObj baseReqObj, Integer formatId) {
        if (latestTemplate != null) {
            // 拷贝旧模板信息，并将版本号加 1，作为最新模板
            Template newLastestTemplate = mapper.map(latestTemplate, Template.class);
            newLastestTemplate.setId(null);
            newLastestTemplate.setIsDeleted(false);
            newLastestTemplate.setPrevId(latestTemplate.getId());
            // 默认为未启用状态
            newLastestTemplate.setStatus(TemplateStatusEnum.NOT_ACTIVATE.getCode());
            newLastestTemplate.setVersion(latestTemplate.getVersion() + 1);
            newLastestTemplate.setGmtInactive(null);
            newLastestTemplate.setGmtActive(null);
            newLastestTemplate.setCreateBy(baseReqObj.getOperationUserId());
            newLastestTemplate.setGmtCreate(LocalDateTime.now());
            newLastestTemplate.setModifiedBy(baseReqObj.getOperationUserId());
            newLastestTemplate.setGmtModified(LocalDateTime.now());

            // 更新旧模板标志
            latestTemplate.setStatus(TemplateStatusEnum.EXPIRE.getCode());
            this.updateById(latestTemplate);
            this.save(newLastestTemplate);

            // 查询模板参数
            LambdaQueryWrapper<TemplateParam> paramQueryWrapper = new LambdaQueryWrapper<>();
            paramQueryWrapper.eq(TemplateParam::getTemplateId, latestTemplate.getId());
            paramQueryWrapper.eq(TemplateParam::getIsDeleted, SystemConsts.NOT_DELETED);
            // 查询出当前模板参数信息
            List<TemplateParam> params = templateParamModel.list(paramQueryWrapper);
            if (null != params && !params.isEmpty()) {
                params.forEach(param -> {
                    // 判断模板参数是否是表格类型，如果是则需要将表格参数也产生新版本
                    if (TemplateParamTypeEnum.TABLE.getCode().equals(param.getType())) {
                        List<TemplateTableParam> templateTableParams = tableParams;
                        // 判断本函数接受的表格参数对应的参数与当前循环的参数是不是一致，一致则不需要查询旧表格数据
                        if (!param.getId().equals(tableId)) {
                            // 先查询旧表格参数
                            templateTableParams = templateTableParamModel
                                .listTemplateTableParamsByTableId(param.getId(), baseReqObj, formatId);
                        }
                        // 先保存新版本的表格信息
                        param.setId(null);
                        param.setTemplateId(newLastestTemplate.getId());
                        templateParamModel.save(param);

                        // 处理新的表格参数信息
                        List<TemplateTableParam> newTableParams = templateTableParams.stream().peek(tableParam -> {
                            tableParam.setId(null);
                            tableParam.setTableId(param.getId());
                        }).collect(Collectors.toList());
                        templateTableParamModel.saveBatch(newTableParams);
                    } else {
                        param.setId(null);
                        param.setTemplateId(newLastestTemplate.getId());
                        templateParamModel.save(param);
                    }
                });
            }
            return newLastestTemplate.getId();
        } else {
            throw new BusinessException("请使用当前合约模板最新版本", "ECCT0105");
        }
    }

}
